Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor package #14

Merged
merged 24 commits into from
Jan 2, 2025
Merged

Refactor package #14

merged 24 commits into from
Jan 2, 2025

Conversation

nckhell
Copy link
Contributor

@nckhell nckhell commented Dec 28, 2024

PR Type

enhancement, tests


Description

  • Refactored multiple calculation modules to use the new Calculation class.
  • Updated test cases across various calculations to align with the new API.
  • Simplified calculation logic by removing legacy helper functions.
  • Improved input and output validation using schemas.
  • Enhanced API controllers to support the new Calculation class.

Changes walkthrough 📝

Relevant files
Enhancement
6 files
calculation_library.ts
Refactor calculation library to remove unused imports and references.

src/calculation_suite/calculations/calculation_library.ts

  • Commented out numerous unused imports and calculation references.
  • Updated the CALCULATIONS object to exclude commented-out calculations.

  • +235/-235
    asrs.ts
    Refactor ASRS calculation to use new Calculation class.   

    src/calculation_suite/calculations/asrs/asrs.ts

  • Refactored ASRS calculation to use the new Calculation class.
  • Simplified calculation logic and removed legacy helper functions.
  • Updated input and output schemas for validation.
  • +33/-72 
    10_meter_walk_test.ts
    Refactor 10 Meter Walk Test calculation to use new Calculation class.

    src/calculation_suite/calculations/10_meter_walk_test/10_meter_walk_test.ts

  • Refactored 10 Meter Walk Test calculation to use the new Calculation
    class.
  • Simplified calculation logic and removed legacy helper functions.
  • Updated input and output schemas for validation.
  • +23/-59 
    bmi.ts
    Refactor BMI US calculation to use new Calculation class.

    src/calculation_suite/calculations/bmi/us/bmi.ts

  • Refactored BMI US calculation to use the new Calculation class.
  • Simplified calculation logic and removed legacy helper functions.
  • Updated input and output schemas for validation.
  • +33/-80 
    beck.ts
    Refactor Beck calculation to use new Calculation class.   

    src/calculation_suite/calculations/beck/beck.ts

  • Refactored Beck calculation to use the new Calculation class.
  • Simplified calculation logic and removed legacy helper functions.
  • Updated input and output schemas for validation.
  • +24/-65 
    calculations.controller.ts
    Update API controller to support new Calculation class.   

    src/api/v1/controllers/calculations.controller.ts

  • Added support for new Calculation class in API endpoints.
  • Updated logic to handle calculations with inputSchema.
  • +14/-0   
    Tests
    10 files
    asrs.test.ts
    Refactor ASRS test to use new Calculation class.                 

    src/calculation_suite/calculations/asrs/asrs.test.ts

  • Replaced legacy helper functions with the new Calculation class.
  • Updated test cases to use the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +102/-124
    acro.test.ts
    Update Acro test to use new Calculation class.                     

    src/calculation_suite/calculations/acro/acro.test.ts

  • Migrated to the new Calculation class for testing.
  • Updated test cases to align with the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +77/-148
    cdlqi.test.ts
    Simplify and clean up CDLQI test cases.                                   

    src/calculation_suite/calculations/cdlqi/cdlqi.test.ts

  • Simplified assertions by removing redundant return statements.
  • Updated test cases to improve readability and consistency.
  • +28/-30 
    10_meter_walk_test.test.ts
    Refactor 10 Meter Walk Test to use new Calculation class.

    src/calculation_suite/calculations/10_meter_walk_test/10_meter_walk_test.test.ts

  • Refactored to use the new Calculation class.
  • Updated test cases to validate the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +43/-71 
    beck.test.ts
    Refactor Beck test to use new Calculation class.                 

    src/calculation_suite/calculations/beck/beck.test.ts

  • Migrated to the new Calculation class for testing.
  • Updated test cases to align with the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +43/-59 
    blcs.test.ts
    Refactor BLCS test to use new Calculation class.                 

    src/calculation_suite/calculations/blcs/blcs.test.ts

  • Refactored to use the new Calculation class.
  • Updated test cases to validate the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +39/-44 
    bwcs.test.ts
    Refactor BWCS test to use new Calculation class.                 

    src/calculation_suite/calculations/bwcs/bwcs.test.ts

  • Refactored to use the new Calculation class.
  • Updated test cases to validate the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +42/-40 
    bmi.test.ts
    Refactor BMI Metric test to use new Calculation class.     

    src/calculation_suite/calculations/bmi/metric/bmi.test.ts

  • Migrated to the new Calculation class for testing.
  • Updated test cases to align with the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +45/-52 
    audit.test.ts
    Simplify and clean up Audit test cases.                                   

    src/calculation_suite/calculations/audit/audit.test.ts

  • Simplified assertions by removing redundant return statements.
  • Improved readability and consistency in test cases.
  • +16/-16 
    bmi.test.ts
    Refactor BMI US test to use new Calculation class.             

    src/calculation_suite/calculations/bmi/us/bmi.test.ts

  • Migrated to the new Calculation class for testing.
  • Updated test cases to align with the new calculation API.
  • Replaced InvalidInputsError with ZodError for validation.
  • +38/-42 
    Additional files (token-limit)
    46 files
    acro.ts
    ...                                                                                                           

    src/calculation_suite/calculations/acro/acro.ts

    ...

    +26/-57 
    age_calc.test.ts
    ...                                                                                                           

    src/calculation_suite/calculations/age_calc/age_calc.test.ts

    ...

    +14/-18 
    bmi.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/metric/bmi.ts

    ...

    +20/-69 
    calculate_subscale_scores.ts
    ...                                                                                                           

    src/calculation_suite/calculations/asrs/helpers/calculate_subscale_scores.ts

    ...

    +22/-41 
    calculate_part_scores.ts
    ...                                                                                                           

    src/calculation_suite/calculations/asrs/helpers/calculate_part_scores.ts

    ...

    +22/-42 
    Calculation.ts
    ...                                                                                                           

    src/api/shared/classes/Calculation.ts

    ...

    +85/-0   
    age_calc.ts
    ...                                                                                                           

    src/calculation_suite/calculations/age_calc/age_calc.ts

    ...

    +18/-59 
    calculate_scores.ts
    ...                                                                                                           

    src/calculation_suite/calculations/acro/helpers/calculate_scores.ts

    ...

    +11/-17 
    calculation_library.test.ts
    ...                                                                                                           

    src/calculation_suite/calculations/calculation_library.test.ts

    ...

    +20/-0   
    acro_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/acro/definition/acro_inputs.ts

    ...

    +42/-0   
    asrs_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/asrs/definition/asrs_output.ts

    ...

    +15/-21 
    bwcs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bwcs/bwcs.ts

    ...

    +16/-30 
    blcs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/blcs/blcs.ts

    ...

    +16/-30 
    beck_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/beck/definition/beck_inputs.ts

    ...

    +48/-0   
    asrs_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/asrs/definition/asrs_inputs.ts

    ...

    +32/-0   
    calculations.service.ts
    ...                                                                                                           

    src/api/shared/services/calculations.service.ts

    ...

    +12/-3   
    10_meter_walk_test_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/10_meter_walk_test/definition/10_meter_walk_test_output.ts

    ...

    +9/-12   
    acro_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/acro/definition/acro_output.ts

    ...

    +11/-15 
    blcs_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/blcs/definition/blcs_inputs.ts

    ...

    +30/-0   
    bwcs_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bwcs/definition/bwcs_inputs.ts

    ...

    +31/-0   
    parse_readme_to_html.ts
    ...                                                                                                           

    src/calculation_suite/helper_functions/parse_readme_to_html/parse_readme_to_html.ts

    ...

    +34/-1   
    bmi.schema.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/us/bmi.schema.ts

    ...

    +32/-0   
    calculations.types.ts
    ...                                                                                                           

    src/types/calculations.types.ts

    ...

    +8/-1     
    bmi.schema.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/metric/bmi.schema.ts

    ...

    +31/-0   
    calculations.controller.ts
    ...                                                                                                           

    src/api/v2/controllers/calculations.controller.ts

    ...

    +9/-0     
    10_meter_walk_test_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/10_meter_walk_test/definition/10_meter_walk_test_inputs.ts

    ...

    +7/-0     
    bmi_form.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/metric/bmi_form.ts

    ...

    +20/-0   
    bmi_form.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/us/bmi_form.ts

    ...

    +20/-0   
    age_calc_inputs.ts
    ...                                                                                                           

    src/calculation_suite/calculations/age_calc/definition/age_calc_inputs.ts

    ...

    +5/-0     
    age_calc_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/age_calc/definition/age_calc_output.ts

    ...

    +5/-6     
    bwcs_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bwcs/definition/bwcs_output.ts

    ...

    +5/-6     
    blcs_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/blcs/definition/blcs_output.ts

    ...

    +5/-6     
    beck_output.ts
    ...                                                                                                           

    src/calculation_suite/calculations/beck/definition/beck_output.ts

    ...

    +5/-6     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/10_meter_walk_test/definition/index.ts

    ...

    +4/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/asrs/definition/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/acro/definition/index.ts

    ...

    +1/-1     
    calculation_library_v3.ts
    ...                                                                                                           

    src/calculation_suite/calculations/calculation_library_v3.ts

    ...

    +6/-0     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/age_calc/definition/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bmi/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/bwcs/definition/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/blcs/definition/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/calculations/beck/definition/index.ts

    ...

    +1/-1     
    index.ts
    ...                                                                                                           

    src/calculation_suite/helper_functions/parse_readme_to_html/index.ts

    ...

    +2/-1     
    package-lock.json
    ...                                                                                                           

    package-lock.json

    ...

    +5282/-8
    package.json
    ...                                                                                                           

    package.json

    ...

    +7/-2     
    jest.config.cjs
    ...                                                                                                           

    jest.config.cjs

    ...

    +18/-0   

    💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information

    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Unused Imports

    The PR introduces a significant number of commented-out imports in the calculation_library.ts file. These should either be removed if not needed or properly documented to avoid confusion and maintain code cleanliness.

    // import { audit } from './audit/audit'
    import { beck } from './beck/beck'
    import { blcs } from './blcs/blcs'
    import { bwcs } from './bwcs/bwcs'
    import { bmi_imperial, bmi_metric } from './bmi'
    // import { breast_q as breast_q_conserving_therapy_pre_and_postoperative } from './breast_q/breast_conserving/breast_q'
    // import { cade_q_sv } from './cade_q'
    // import { caregiver_strain_index } from './caregiver_strain_index/caregiver_strain_index'
    // import { cat } from './cat/cat'
    // import { ccq } from './ccq/ccq'
    // import { cdlqi } from './cdlqi/cdlqi'
    // import { CHA2DS2_VASc_Score } from './CHA2DS2_VASc_Score/CHA2DS2_VASc_Score'
    // import { chc_preop_brochure_triage } from './chc'
    // import { femmes_enceintes_triage } from './chc/femmes_enceintes/triage/triage'
    // import { comi_back, comi_neck } from './comi'
    // import { compass_31 } from './compass_31/compass_31'
    // import { constant_murley_score_orthotoolkit } from './constant_murley_score'
    // import { core_om } from './core_om/core_om'
    // import { cpdi } from './cpdi/cpdi'
    // import { csi } from './csi/csi'
    // import { dast_10 } from './dast_10/dast_10'
    // import { demo_calculation } from './demo_calculation/demo_calculation'
    // import { difference_calc } from './difference_calc/difference_calc'
    // import { dlqi } from './dlqi/dlqi'
    // import { dn4 } from './dn4/dn4'
    // import { dri } from './dri/dri'
    // import {
    //   eortc_qlq_br23,
    //   eortc_qlq_br45,
    //   eortc_qlq_c30,
    //   eortc_qlq_cr29,
    //   eortc_qlq_lc13,
    //   eortc_qlq_lc29,
    //   eortc_qlq_pr25,
    // } from './eortc'
    // import { epic_26 } from './epic_26/epic_26'
    // import { eq5d_3l, eq5d_5l } from './eq5d'
    // import { ess } from './ess/ess'
    // import { faam } from './faam/faam'
    // import { short_fes_i } from './fes_i'
    // import { fnd } from './fnd/fnd'
    // import { foot_function_index_5pt } from './foot_function_index'
    // import {
    //   forgotten_joint_score_hip,
    //   forgotten_joint_score_knee,
    // } from './forgotten_joint_score_12'
    // import { pss_4 } from './pss_4/pss_4'
    // import { stop_bang } from './stop_bang/stop_bang'
    // import { gad_2 } from './GAD_2/gad_2'
    // import { gad_7 } from './GAD_7/gad_7'
    // import { ghq_12 } from './ghq_12/ghq_12'
    // import { hads } from './hads/HADS_score'
    // import { harris_hip_score } from './harris_hip_score/harris_hip_score'
    // import { hoos_extended } from './hoos_extended/hoos_extended'
    // import { hoos_ps } from './hoos_ps/hoos_ps'
    // import { hos } from './hos/hos'
    // import { hrqol_4 } from './hrqol'
    // import { ias } from './ias/ias'
    // import { IBD_control } from './IBD_control/IBD_control'
    // import { ibd_disk_total_score } from './ibd_disk_total_score/ibd_disk_total_score'
    // import { iief5 } from './iief5/iief5'
    // import { ikdc } from './ikdc/ikdc'
    // import { ipss } from './ipss/ipss'
    // import { isi } from './isi/isi'
    // import { k_bild } from './k_bild/k_bild'
    // import { KCCQ_12 } from './KCCQ_12/KCCQ_12'
    // import { koos_ps } from './koos_ps/koos_ps'
    // import { math_divide, math_multiply, math_subtract, math_sum } from './math'
    // import { mds_updrs } from './mds_updrs/mds_updrs'
    // import { mini_best_test } from './mini_best_test/mini_best_test'
    // import { mfis } from './mfis/mfis'
    // import { mmse } from './mmse/mmse'
    // import { moca } from './moca/moca'
    // import { modified_caregiver_strain_index } from './modified_caregiver_strain_index/modified_caregiver_strain_index'
    // import { mpi } from './mpi/mpi'
    // import { msq } from './msq/msq'
    // import { ndi } from './ndi/ndi'
    // import { oas } from './oas/oas'
    // import { ompq, ompq_10 } from './orebro'
    // import { oswestry } from './oswestry/oswestry'
    // import { oxford_hip_score } from './oxford_hip_score/oxford_hip_score'
    // import { oxford_knee_score } from './oxford_knee_score/oxford_knee_score'
    // import { packyears } from './packyears/packyears'
    // import { panss_6 } from './panss_6/panss_6'
    // import { paq_c } from './paq_c/paq_c'
    // import { pci } from './pci/pci'
    // import { pcl_5 } from './pcl_5/pcl_5'
    // import { pcs } from './pcs/pcs'
    // import { pdi } from './pdi/pdi'
    // import { pdq_8 } from './pdq_8/pdq_8'
    // import { phq_2 } from './phq_2/phq_2'
    // import { phq_4 } from './phq_4/phq_4'
    // import { phq_8 } from './phq_8/phq_8'
    // import { phq_9 } from './phq_9/phq_9'
    // import { physical_activity_measurement } from './physical_activity_measurements/physical_activity_measurements'
    // import { posas_observer } from './posas_observer/posas_observer'
    // import { posas_patient } from './posas_patient/posas_patient'
    // import { pro_ctcae } from './pro_ctcae/pro_ctcae'
    // import { PRO2 } from './pro2/pro2'
    // import { promis_10 } from './promis_10/promis_10'
    // import { prtee } from './prtee/prtee'
    // import { psk } from './psk/psk'
    // import { qol_stoma } from './qol_stoma/qol_stoma'
    // import { quickdash } from './quickdash/quickdash'
    // import { sccai } from './sccai/sccai'
    // import { scl90 } from './scl90/scl90'
    // import { scl90r } from './scl90r/scl90r'
    // import { sdq } from './sdq/sdq'
    // import { sf12, sf36 } from './short form surveys'
    // import { spadi } from './spadi/spadi'
    // import { simple_shoulder_test } from './sst/simple_shoulder_test'
    // import { start_back_screening_tool } from './start_back_screening_tool/start_back_screening_tool'
    // import { tampa } from './tampa/tampa'
    // import { visa_a, visa_g, visa_p } from './visa'
    // import { yp_core } from './yp_core/yp_core'
    // import { zarit_12 } from './zarit_12/zarit_12'
    import { acro } from './acro/acro'
    // import { korq } from './korq/korq'
    // import { mlks } from './mlks/mlks'
    // import { psqi } from './psqi/psqi'
    
    export const CALCULATIONS: CalculationsLibraryType = {
      age_calc,
      acro,
      asrs,
      // audit,
      beck,
      bmi: bmi_metric,
      bmi_imperial,
      blcs,
      bwcs,
      // breast_q_conserving_therapy_pre_and_postoperative,
      // cade_q_sv,
      // caregiver_strain_index,
      // cat,
      // ccq,
      // cdlqi,
      // CHA2DS2_VASc_Score,
      // chc_preop_brochure_triage,
      // femmes_enceintes_triage,
      // comi_back,
      // comi_neck,
      // compass_31,
      // constant_murley_score_orthotoolkit,
      // core_om,
      // cpdi,
      // csi,
      // dast_10,
      // demo_calculation,
      // difference_calc,
      // dlqi,
      // dn4,
      // dri,
      // eortc_qlq_br23,
      // eortc_qlq_br45,
      // eortc_qlq_c30,
      // eortc_qlq_cr29,
      // eortc_qlq_lc13,
      // eortc_qlq_lc29,
      // eortc_qlq_pr25,
      // epic_26,
      // eq5d_3l,
      // eq5d_5l,
      // ess,
      // faam,
      // fnd,
      // foot_function_index_5pt,
      // forgotten_joint_score_hip,
      // forgotten_joint_score_knee,
      // gad_2,
      // gad_7,
      // ghq_12,
      // hads,
      // harris_hip_score,
      // hoos_extended,
      // hoos_ps,
      // hos,
      // hrqol_4,
      // ias,
      // IBD_control,
      // ibd_disk_total_score,
      // iief5,
      // ikdc,
      // isi,
      // ipss,
      // k_bild,
      // KCCQ_12,
      // koos_ps,
      // korq,
      // math_divide,
      // math_multiply,
      // math_sum,
      // math_subtract,
      // mds_updrs,
      // mini_best_test,
      // mfis,
      // mlks,
      // mmse,
      // moca,
      // modified_caregiver_strain_index,
      // mpi,
      // msq,
      // ndi,
      // oas,
      // ompq,
      // ompq_10,
      // oswestry,
      // oxford_hip_score,
      // oxford_knee_score,
      // packyears,
      // panss_6,
      // paq_c,
      // pci,
      // pcl_5,
      // pcs,
      // pdi,
      // pdq_8,
      // phq_2,
      // phq_4,
      // phq_8,
      // phq_9,
      // physical_activity_measurement,
      // posas_observer,
      // posas_patient,
      // pro_ctcae,
      // PRO2,
      // promis_10,
      // prtee,
      // psk,
      // psqi,
      // pss_4,
      // qol_stoma,
      // quickdash,
      // sccai,
      // scl90,
      // scl90r,
      // sdq,
      // sf12,
      // sf36,
      // short_fes_i,
      // spadi,
      // simple_shoulder_test,
      // start_back_screening_tool,
      // stop_bang,
      // tampa,
      ten_meter_walk_test,
      // visa_a,
      // visa_g,
      // visa_p,
      // yp_core,
      // zarit_12,
    Error Handling

    The new tests for asrs calculation rely on ZodError for validation. Ensure that the error handling mechanism is robust and provides meaningful feedback to users or developers when invalid inputs are encountered.

    import { ASRS_PARTS, ASRS_SUBSCALES } from './definition'
    import { Calculation } from '../../../api/shared/classes/Calculation'
    import { ZodError } from 'zod'
    
    const asrs_calculation = new Calculation(asrs)
    
    describe('asrs', function () {
      it('asrs calculation function should be available as a calculation', function () {
        expect(CALCULATIONS).to.have.property('asrs')
      })
    
      describe('basic assumptions', function () {
        const outcome = asrs_calculation.calculate({ payload: best_response })
    
        it('should have the expected calculation ids', function () {
          const EXPECTED_CALCULATION_IDS = [
            'ASRS_PART_A_SCORE',
            'ASRS_PART_B_SCORE',
            'ASRS_TOTAL_SCORE',
            'ASRS_INATTENTIVE_SUBSCALE_SCORE',
            'ASRS_HYPERACTIVE_IMPULSIVE_SUBSCALE_MOTOR_SCORE',
            'ASRS_HYPERACTIVE_IMPULSIVE_SUBSCALE_VERBAL_SCORE',
          ]
          const configured_calculation_ids = Object.keys(outcome)
    
          expect(configured_calculation_ids).to.eql(EXPECTED_CALCULATION_IDS)
        })
      })
    
      describe('validation', function () {
        describe('the score includes the correct input fields', function () {
          it('should have all the expected input ids configured', function () {
            const EXPECTED_INPUT_IDS = [
              'Q01',
              'Q02',
              'Q03',
              'Q04',
              'Q05',
              'Q06',
              'Q07',
              'Q08',
              'Q09',
              'Q10',
              'Q11',
              'Q12',
              'Q13',
              'Q14',
              'Q15',
              'Q16',
              'Q17',
              'Q18',
            ]
    
            const configured_input_ids = Object.keys(
              asrs_calculation.inputSchema.shape
            )
    
            expect(EXPECTED_INPUT_IDS).to.eql(configured_input_ids)
          })
        })
    
        describe('ASRS parts', function () {
          describe('Part A', function () {
            it('should have the expected input ids', function () {
              const EXPECTED_INPUT_IDS = ['Q01', 'Q02', 'Q03', 'Q04', 'Q05', 'Q06']
    
              expect(EXPECTED_INPUT_IDS).to.eql(ASRS_PARTS.PART_A)
            })
          })
    
          describe('Part B', function () {
            it('should have the expected input ids', function () {
              const EXPECTED_INPUT_IDS = [
                'Q07',
                'Q08',
                'Q09',
                'Q10',
                'Q11',
                'Q12',
                'Q13',
                'Q14',
                'Q15',
                'Q16',
                'Q17',
                'Q18',
              ]
    
              expect(EXPECTED_INPUT_IDS).to.eql(ASRS_PARTS.PART_B)
            })
          })
        })
    
        describe('ASRS subscales', function () {
          describe('Inattentive subscale', function () {
            it('should have the expected input ids', function () {
              const EXPECTED_INPUT_IDS = [
                'Q01',
                'Q02',
                'Q03',
                'Q04',
                'Q07',
                'Q08',
                'Q09',
                'Q10',
                'Q11',
              ]
    
              expect(EXPECTED_INPUT_IDS).to.eql(ASRS_SUBSCALES.INATTENTIVE_SUBSCALE)
            })
          })
    
          describe('Hyperactive/Impulsive subscale (Motor)', function () {
            it('should have the expected input ids', function () {
              const EXPECTED_INPUT_IDS = ['Q05', 'Q06', 'Q12', 'Q13', 'Q14']
    
              expect(EXPECTED_INPUT_IDS).to.eql(
                ASRS_SUBSCALES.HYPERACTIVE_IMPULSIVE_SUBSCALE_MOTOR
              )
            })
          })
    
          describe('Hyperactive/Impulsive subscale (Verbal)', function () {
            it('should have the expected input ids', function () {
              const EXPECTED_INPUT_IDS = ['Q15', 'Q16', 'Q17', 'Q18']
    
              expect(EXPECTED_INPUT_IDS).to.eql(
                ASRS_SUBSCALES.HYPERACTIVE_IMPULSIVE_SUBSCALE_VERBAL
              )
            })
          })
        })
    
        describe('when an answer is below the expected range', function () {
          it('should throw an InvalidInputsError', function () {
            expect(() =>
              asrs_calculation.calculate({
                payload: {
                  Q01: -1,
                },
              })
            ).to.throw(ZodError)
          })
        })
    
        describe('when an answer is above the expected range', function () {
          it('should throw an InvalidInputsError', function () {
            expect(() =>
              asrs_calculation.calculate({
                payload: {
                  Q01: 5,
                },
              })
            ).to.throw(ZodError)
          })
        })
    
        describe('when there are non-numerical answers', function () {
          it('should throw an InvalidInputsError', function () {
            expect(() =>
              asrs_calculation.calculate({
                payload: {
                  Q01: "I'm not a number",
                },
              })
            ).to.throw(ZodError)
          })
    Input Validation

    The 10_meter_walk_test calculation tests include validation for missing or invalid inputs. Verify that the validation schema and error messages are comprehensive and user-friendly.

    import { Calculation } from '../../../api/shared/classes/Calculation'
    import { ZodError } from 'zod'
    
    const tmwt_calculation = new Calculation(ten_meter_walk_test)
    
    describe('ten_meter_walk_test', function () {
      it('ten_meter_walk_test calculation function should be available as a calculation', function () {
        expect(CALCULATIONS).to.have.property('ten_meter_walk_test')
      })
    
      describe('specific_steps_ten_meter_walk_test_calc', function () {
        describe('basic assumptions', function () {
          const outcome = tmwt_calculation.calculate({
            payload: {
              TRIAL_1: 10,
            },
          })
    
          it('should return 3 calculation results', function () {
            const AMOUNT_OF_RESULTS = 3
            expect(Object.keys(outcome)).to.have.length(AMOUNT_OF_RESULTS)
          })
    
          it('should the calculation names', function () {
            const EXPECTED_CALCULATION_IDS = [
              'TMWT_MEAN_IN_SECONDS',
              'TMWT_MEAN_IN_METERS_PER_SECOND',
              'TMWT_MEAN_IN_KILOMETERS_PER_HOUR',
            ]
    
            const EXTRACTED_CALCULATION_IDS_FROM_RESULT = Object.keys(outcome)
    
            expect(EXTRACTED_CALCULATION_IDS_FROM_RESULT).to.eql(
              EXPECTED_CALCULATION_IDS
            )
          })
        })
    
        describe('validations', function () {
          describe('the score includes the correct input fields', function () {
            it('should have all the expected input ids configured', function () {
              const EXPECTED_INPUT_IDS = ['TRIAL_1', 'TRIAL_2', 'TRIAL_3']
              const configured_calculation_input_ids = Object.keys(
                tmwt_calculation.inputSchema.shape
              )
    
              expect(EXPECTED_INPUT_IDS).to.eql(configured_calculation_input_ids)
            })
          })
    
          describe('when called with a response with answers out of the expected [0,4] range', function () {
            describe('when an answer is not a number', function () {
              it('should throw an error', function () {
                expect(() =>
                  tmwt_calculation.calculate({
                    payload: {
                      TRIAL_1: 'hello',
                    },
                  })
                ).to.throw(ZodError)
              })
            })
    
            describe('when the response is empty', function () {
              it('should throw an error', function () {
                expect(() =>
                  tmwt_calculation.calculate({
                    payload: {},
                  })
                ).to.throw(ZodError)
              })
            })
          })
        })
    
        describe('score calculation', function () {
          describe('when called with a single trial', function () {
            const outcome = tmwt_calculation.calculate({
              payload: {
                TRIAL_1: 7, // Took the patient 7 seconds to walk 10 meters
              },
            })
    
            it('should return the correct result for the mean in seconds', function () {
              const EXPECTED_RESULT = 7
              expect(outcome.TMWT_MEAN_IN_SECONDS).to.eql(EXPECTED_RESULT)
            })
    
            it('should return the correct result for the mean in meters per second', function () {
              const EXPECTED_RESULT = 1.43
              expect(outcome.TMWT_MEAN_IN_METERS_PER_SECOND).to.eql(EXPECTED_RESULT)
            })
    
            it('should return the correct result for the mean in kilometers per hour', function () {
              const EXPECTED_RESULT = 5.14
              expect(outcome.TMWT_MEAN_IN_KILOMETERS_PER_HOUR).to.eql(
                EXPECTED_RESULT
              )
            })
          })

    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Score
    Possible issue
    Add validation to ensure input data contains only numbers before performing calculations

    Validate that Object.values(data) only contains numbers before performing
    calculations to prevent runtime errors.

    src/calculation_suite/calculations/10_meter_walk_test/10_meter_walk_test.ts [19-21]

    -const TOTAL_DISTANCE = Object.values(data).length * DISTANCE_PER_TRIAL
    -const TOTAL_TIME = R.sum(Object.values(data))
    +const values = Object.values(data);
    +if (!values.every(value => typeof value === 'number')) {
    +  throw new Error('Invalid input: all values must be numbers');
    +}
    +const TOTAL_DISTANCE = values.length * DISTANCE_PER_TRIAL;
    +const TOTAL_TIME = R.sum(values);
    Suggestion importance[1-10]: 10

    Why: This suggestion ensures that the input data is validated before performing calculations, preventing potential runtime errors caused by invalid inputs. It addresses a critical issue and significantly improves the robustness of the code.

    10
    Fix invalid Zod schema method for date validation to ensure proper input validation

    Replace z.string().date() with z.string().refine() to ensure better validation of
    date strings, as z.string().date() is not a valid Zod method.

    src/calculation_suite/calculations/age_calc/definition/age_calc_inputs.ts [20-22]

     export const InputSchema = z.object({
    -  date_of_birth: z.string().date(),
    +  date_of_birth: z.string().refine(value => !isNaN(Date.parse(value)), {
    +    message: "Invalid date format",
    +  }),
     })
    Suggestion importance[1-10]: 10

    Why: The suggestion addresses a critical issue with the use of an invalid Zod method (z.string().date()) and replaces it with a valid and functional alternative. This ensures proper validation of date strings, which is essential for the correctness of the input schema.

    10
    Add a check to prevent division by zero when calculating BMI

    Validate that numeric_height_in_m is not zero before performing the division to
    prevent potential division by zero errors.

    src/calculation_suite/calculations/bmi/metric/bmi.ts [16]

    -const BMI = data.weight / numeric_height_in_m ** 2
    +const BMI = numeric_height_in_m > 0 ? data.weight / numeric_height_in_m ** 2 : 0
    Suggestion importance[1-10]: 10

    Why: This suggestion prevents a critical runtime error (division by zero) when calculating BMI. It ensures the calculation is safe and avoids potential crashes, making it highly impactful.

    10
    Limit the range of height_inches to valid values for inches in a foot

    Add a validation rule to ensure that height_inches does not exceed 11, as it
    represents inches in a foot and should be within a valid range.

    src/calculation_suite/calculations/bmi/us/bmi.schema.ts [6]

    -height_inches: z.number().min(0).max(50),
    +height_inches: z.number().min(0).max(11),
    Suggestion importance[1-10]: 10

    Why: This suggestion corrects a logical error in the schema by limiting height_inches to a valid range (0-11). It ensures data integrity and prevents invalid input, which is critical for accurate calculations.

    10
    Add a check to handle cases where the calculation function returns null

    Ensure that the calculate method in the Calculation class handles cases where
    this._calculate returns null to avoid unexpected behavior or runtime errors.

    src/api/shared/classes/Calculation.ts [78-80]

    -return this._calculate({
    +const result = this._calculate({
       data: parsedData,
     })
    +if (result === null) {
    +  throw new Error('Calculation returned null');
    +}
    +return result;
    Suggestion importance[1-10]: 9

    Why: This suggestion adds a safeguard to handle cases where the calculation function returns null, preventing potential runtime errors and ensuring robustness. It is highly relevant and directly improves the reliability of the calculate method.

    9
    Ensure null values are safely handled when mapping over inputs to calculate standardized scores

    Handle cases where _.map in standardized_input_scores might return null values due
    to undefined or invalid input definitions, as this could lead to unexpected behavior
    in subsequent calculations.

    src/calculation_suite/calculations/asrs/helpers/calculate_part_scores.ts [19-33]

    -const standardized_input_scores = _.map(inputs_in_part, (_i, key) => {
    +const standardized_input_scores = _.compact(_.map(inputs_in_part, (_i, key) => {
       const input_definition = ASRS_INPUTS.find(
         inputDef => inputDef.input_id === key
       )
    -  if (input_definition === undefined || _i === undefined) return null
    -  if (input_definition.positive_scores.includes(_i)) return 1
    -  return 0
    -}).filter(v => v !== null)
    +  if (!input_definition || _i === undefined) return null
    +  return input_definition.positive_scores.includes(_i) ? 1 : 0
    +}))
    Suggestion importance[1-10]: 9

    Why: The suggestion enhances the reliability of the mapping logic by using _.compact to handle null values safely. This ensures that invalid or undefined input definitions do not propagate into subsequent calculations, reducing the risk of errors.

    9
    Add a fallback for unmapped values in the preprocessing function to avoid undefined results

    Ensure that the preprocess_beck_response function handles cases where the
    RAW_ANSWER_TO_VALUE_DICT does not contain a mapping for a given value, to prevent
    returning undefined values.

    src/calculation_suite/calculations/beck/beck.ts [26-27]

    -const value_in_dict = RAW_ANSWER_TO_VALUE_DICT[String(val)]
    +const value_in_dict = RAW_ANSWER_TO_VALUE_DICT[String(val)] ?? 0
     return value_in_dict
    Suggestion importance[1-10]: 9

    Why: This suggestion addresses a potential issue where undefined values could be returned if a mapping is missing in RAW_ANSWER_TO_VALUE_DICT. Adding a fallback ensures robustness and prevents unexpected behavior during calculations.

    9
    Add a type check to ensure keys are strings before filtering against the required input IDs

    Ensure that the _.filter function correctly filters based on the
    INPUT_IDS_NEEDED_FOR_SCORING array, as the current implementation may not handle
    cases where inputs_with_answers keys are not strings or have unexpected formats.

    src/calculation_suite/calculations/acro/helpers/calculate_scores.ts [14-18]

     const valid_answers_in_subscale = _.compact(
       _.filter(inputs_with_answers, (_i, key) =>
    -    INPUT_IDS_NEEDED_FOR_SCORING.includes(key)
    +    typeof key === 'string' && INPUT_IDS_NEEDED_FOR_SCORING.includes(key)
       )
     )
    Suggestion importance[1-10]: 8

    Why: The suggestion improves the robustness of the filtering logic by ensuring that keys are strings before comparing them against the required input IDs. This prevents potential runtime errors or unexpected behavior when keys are not strings, which is a valid concern in dynamic input scenarios.

    8
    General
    Handle unsupported calculation execution cases to prevent silent failures

    Add a response or error handling mechanism for the execute_calculation method when
    requested_calculation contains inputSchema to avoid silent failures.

    src/api/v1/controllers/calculations.controller.ts [118-120]

     if ('inputSchema' in requested_calculation) {
    -  return
    +  res.status(StatusCodes.BAD_REQUEST).send({
    +    error: {
    +      message: 'Calculation execution not supported for this schema',
    +    },
    +  });
    +  return;
     }
    Suggestion importance[1-10]: 8

    Why: Adding error handling for unsupported calculation execution cases improves the clarity and reliability of the API by providing meaningful feedback to the user instead of silently failing. This is a valuable enhancement to the existing code.

    8
    Restrict valid answers to numeric values within the expected range for accurate calculations

    Ensure that valid_answers only includes numeric values within the expected range to
    avoid incorrect calculations.

    src/calculation_suite/calculations/blcs/blcs.ts [12]

    -const valid_answers = Object.values(data).filter(is_numeric)
    +const valid_answers = Object.values(data).filter(value => is_numeric(value) && value >= 0 && value <= 10)
    Suggestion importance[1-10]: 8

    Why: This suggestion improves the accuracy of the calculation by ensuring only numeric values within the expected range are considered. It enhances data validation and prevents incorrect results.

    8
    Add a safeguard to handle missing or incomplete input data in the calculate_scores function

    Ensure that the calculate_scores function handles cases where data is missing or
    incomplete to avoid returning undefined results.

    src/calculation_suite/calculations/acro/acro.ts [11]

    -const PHYSICAL_SUBSCALE_SCORE = calculate_scores(data, 'PHYSICAL_SUBSCALE')
    +const PHYSICAL_SUBSCALE_SCORE = data
    +  ? calculate_scores(data, 'PHYSICAL_SUBSCALE')
    +  : null;
    Suggestion importance[1-10]: 7

    Why: This suggestion adds a safeguard to handle missing or incomplete input data, which enhances the reliability of the calculate_scores function. While it is a useful improvement, its impact is slightly less critical compared to other suggestions.

    7
    Enhance test cases to ensure ZodError includes meaningful error messages for debugging

    Verify that the ZodError thrown in invalid input tests includes specific error
    messages to help identify the root cause of the validation failure.

    src/calculation_suite/calculations/asrs/asrs.test.ts [145-150]

     expect(() =>
       asrs_calculation.calculate({
         payload: {
           Q01: -1,
         },
       })
    -).to.throw(ZodError)
    +).to.throw(ZodError, /Invalid input/)
    Suggestion importance[1-10]: 7

    Why: Adding specific error messages to the ZodError validation tests improves the clarity and debugging experience. While not critical, this enhancement provides better insight into validation failures.

    7

    @nckhell nckhell changed the title [DRAFT]: Refactor and next version of the API Refactor package Jan 2, 2025
    @nckhell nckhell merged commit 94a8418 into main Jan 2, 2025
    1 check passed
    @nckhell nckhell deleted the v2 branch January 2, 2025 08:46
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    1 participant